package com.KMeans;

import java.util.Comparator;
import java.util.List;
import java.util.Random;

public class Utils {
	
	/**
	 * 默认数据集合比较器
	 * @param dimension
	 * @return
	 */
	public static Comparator<Cluster> defaultClusterComparator(){
		class DefaultComparator implements Comparator<Cluster>{
			
			public int compare(Cluster o1, Cluster o2) {
				int result = 0;
				double o1Distance = getDistance(o1.getCenterItem(), o1.getDimensionKeys());
				double o2Distance = getDistance(o2.getCenterItem(), o1.getDimensionKeys());
				if(o1Distance < o2Distance)
					result = 1;
				else if(o1Distance > o2Distance)
					result = -1;
				return result;
			}
		}
		
		DefaultComparator comparator = new DefaultComparator();
		return comparator;
	}
	
	public static Comparator<Item> defaultItemComparator(int dimension){
		return defaultItemComparator(createDimensionKeys(dimension));
	}
	
	/**
	 * 默认节点数据比较器
	 * @param dimension
	 * @return
	 */
	public static Comparator<Item> defaultItemComparator(int[] dimensionKeys){
		class DefaultComparator implements Comparator<Item>{
			
			private int dimension;
			
			private int[] dimensionKeys;
			
			public DefaultComparator(int[] dimensionKeys) {
				this.dimension     = dimensionKeys.length;
				this.dimensionKeys = dimensionKeys; 
			}
			
			public int compare(Item o1, Item o2) {
				int result = 0;
				double o1Distance = getDistance(o1, dimensionKeys);
				double o2Distance = getDistance(o2, dimensionKeys);
				if(o1Distance > o2Distance)
					result = 1;
				else if(o1Distance < o2Distance)
					result = -1;
				return result;
			}
		}
		
		DefaultComparator comparator = new DefaultComparator(dimensionKeys);
		return comparator;
	}
	
	
	public static Item getBasePoint(int dimension){
		return getBasePoint(createDimensionKeys(dimension));
	}
	
	/**
	 * 获取原点
	 * @param dimension
	 * @return
	 */
	public static Item getBasePoint(int[] dimensionKeys){
		Item basePoint = new CenterItem(); 
		for(int i = 0; i < dimensionKeys.length; i++){
			basePoint.setData(dimensionKeys[i], 0);
		}
		return basePoint;
	}

	public static double getDistance(Item source, int dimension){
		return getDistance(source, null, createDimensionKeys(dimension));
	}
	
	/**
	 * 获取当前点到原点的欧几里得距离
	 * @param source
	 * @param dimension
	 * @return
	 */
	public static double getDistance(Item source, int[] dimensionKeys){
		return getDistance(source, null, dimensionKeys);
	}
	
	public static double getDistance(Item source, Item target, int dimension){
		return getDistance(source, target, createDimensionKeys(dimension));
	}
	
	
	/**
	 * 获取欧几里得距离
	 * @param source 起点
	 * @param target 终点
	 * @param dimension 维数
	 * @return
	 */
	public static double getDistance(Item source, Item target, int[] dimensionKeys){
		
		if(target == null)
			target = getBasePoint(dimensionKeys);
		
		double distance = -1;
		if(dimensionKeys.length == 1){
			distance = Math.abs(source.getData(dimensionKeys[0]) - target.getData(dimensionKeys[0]));
		}else{
			double difference = 0;
			for(int i = 0; i < dimensionKeys.length; i++){
				try{
					difference += Math.pow(source.getData(dimensionKeys[i]) - target.getData(dimensionKeys[i]), 2);
				}catch(Exception e){
					System.out.println("index:" + i);
					System.out.println("target:"+target.getData(dimensionKeys[i]));
					System.out.println("source:"+source.getData(0));
					System.out.println("source:"+source.getData(1));
					System.out.println("source:"+source.getData(2));
					e.printStackTrace();
				}
			}
			if(difference >= 0)
				distance = Math.sqrt(difference);
		}
		return distance;
	}
	
	
	/**
	 * 获取随机数
	 * @param centers
	 * @param counts
	 * @return
	 */
	public static int[] getRandom(int centers, int counts){
		int[] rands = new int[centers];
		
		for(int i = 0; i < rands.length; i++){
			rands[i] = -1;
		}
		
		for(int i = 0; i < rands.length; i++){
			Random random = new Random();
			int temp = random.nextInt(counts);
			while(!checkRand(rands, temp)){
				temp = random.nextInt(counts);
			}
			rands[i] = temp;
		}
		
		return rands;
	}
	
	/**
	 * 检查随机数是否有重复值
	 * @param random
	 * @param data
	 * @return
	 */
	private static boolean checkRand(int[] random, int data){
		boolean result = true;
		for(int randomData : random){
			if(randomData > 0 && randomData == data){
				result = false;
			}
		}
		
		return result;
	}
	
	/**
	 * 比较两个Item是否相等
	 * @param source
	 * @param target
	 * @param dimension
	 * @return
	 */
	public static boolean itemEqualsTo(Item source, Item target, int[] dimensionKeys){
		boolean result = true;
		for(int i = 0; i < dimensionKeys.length; i++){
			if(source.getData(dimensionKeys[i]) != target.getData(dimensionKeys[i])){
				result = false;
				break;
			}
		}
		return result;
	}
	
	/**
	 * 初始化维度映射
	 * @param dimension
	 * @return
	 */
	public static int[] createDimensionKeys(int dimension){
		int[] dimensionKeys = new int[dimension];
		for(int i = 0; i < dimension; i++){
			dimensionKeys[i] = i;
		}
		return dimensionKeys;
	}
	
	
	public static void copyItemData(Item source, Item target, int dimension){
		copyItemData(source, target, createDimensionKeys(dimension));
	}
	
	/**
	 * 将目标Item中的值复制到另外一个Item中
	 * @param source
	 * @param target
	 * @param dimension
	 */
	public static void copyItemData(Item source, Item target, int[] dimensionKeys){
		for(int i = 0; i < dimensionKeys.length; i++){
			target.setData(dimensionKeys[i], source.getData(dimensionKeys[i]));
		}
	}
	
	
	public static String getItemString(Item item, int[] dimensionKeys){
		StringBuilder sb = new StringBuilder("[");
		for(int i = 0; i < dimensionKeys.length; i++){
			if(i != 0)
				sb.append(",").append(item.getData(dimensionKeys[i]));
			else
				sb.append(item.getData(dimensionKeys[i]));
		}
		return sb.append("] ").toString();
	}
	
	
	public static String getItemString(Item item, int dimension){
		return getItemString(item, createDimensionKeys(dimension));
	}
	
	public static void printItem(Item item, int dimension){
		System.out.print(getItemString(item, dimension));
	}
	
	public static void printItem(Item item, int[] dimensionKeys){
		System.out.print(getItemString(item, dimensionKeys));
	}
	
	public static <T extends Item> void printCluster(Cluster<T>[] clusters){
		for(Cluster<T> cluster : clusters){
			List<T> items = cluster.getItems();
			for(Item item : items){
				Utils.printItem(item, cluster.getDimensionKeys());
			}
			System.out.println();
		}
		System.out.println();
	}
	
}
